
#ifndef ROPUFU_MATLAB_HPP_INCLUDED
#define ROPUFU_MATLAB_HPP_INCLUDED

#include <array>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <stack>
#include <vector>

namespace ropufu
{
    struct matlab
    {
        typedef std::chrono::high_resolution_clock clock_type;

    private:
        static std::stack<double> s_seconds;

        matlab() = delete;

    public:
        template <typename T>
        static std::vector<T> linspace(T a, T b, std::size_t n)
        {
            if (n == 0) return std::vector<T>(0);

            std::vector<T> result(n);
            n--;
            result[0] = a;
            result[n] = b;
            for (std::size_t j = 1; j < n; j++) result[j] = ((n - j) * a + j * b) / (n);
            return result;
        }

        template <typename T>
        static std::vector<T> logspace(T a, T b, std::size_t n)
        {
            if (n == 0) return std::vector<T>(0);
            T log_a = std::log(a);
            T log_b = std::log(b);

            std::vector<T> result(n);
            n--;
            result[0] = a;
            result[n] = b;
            for (std::size_t j = 1; j < n; j++) result[j] = std::exp(((n - j) * log_a + j * log_b) / (n));
            return result;
        }

        template <typename T>
        static std::vector<T> expspace(T a, T b, std::size_t n)
        {
            if (n == 0) return std::vector<T>(0);
            T exp_a = std::exp(a);
            T exp_b = std::exp(b);

            std::vector<T> result(n);
            n--;
            result[0] = a;
            result[n] = b;
            for (std::size_t j = 1; j < n; j++) result[j] = std::log(((n - j) * exp_a + j * exp_b) / (n));
            return result;
        }

        static void tic()
        {
            double seconds_tic = static_cast<double>(clock_type::now().time_since_epoch().count() * clock_type::period::num) / clock_type::period::den;
            matlab::s_seconds.push(seconds_tic);
        }

        static void toc()
        {
            double seconds_toc = static_cast<double>(clock_type::now().time_since_epoch().count() * clock_type::period::num) / clock_type::period::den;
            double seconds_tic = matlab::s_seconds.top();
            matlab::s_seconds.pop();

            double dt = seconds_toc - seconds_tic;
            auto hours = static_cast<std::int_fast32_t>(dt / (60 * 60));
            dt -= (60 * 60) * hours;
            auto minutes = static_cast<std::int_fast32_t>(dt / (60));
            dt -= (60) * minutes;
            
            std::cout << "Elapsed time is ";
            if (hours != 0) std::cout << hours << " hour" << (hours == 1 ? "" : "s") << " ";
            if (minutes != 0) std::cout << minutes << " minute" << (minutes == 1 ? "" : "s") << " ";
            std::cout << dt << " seconds." << std::endl;
        }
    };

    std::stack<double> matlab::s_seconds = std::stack<double>();
}

#endif // ROPUFU_MATLAB_HPP_INCLUDED
